#include "image.h"
	
/**
 * Given a Gaussian image, it writes the relative TIFF image on a specified file
 * @param image image to be written on file
 * @param dest pathname of the output TIFF file
 * @param width width of the image
 * @param height height of the image
 */

void writeImage (unsigned char *image, char *dest, int width, int height) {
	
    TIFF *out = TIFFOpen (dest, "w");
    tsize_t linebytes;
    uint32 row = 0;
    
    /* buffer used to store the row of pixel information for writing to file */
    unsigned char *buf = NULL;
    
    /* 8bit image */
    int sampleperpixel = 1;
	
    TIFFSetField (out, TIFFTAG_IMAGEWIDTH, width);	/* set the width of the image */
    TIFFSetField (out, TIFFTAG_IMAGELENGTH, height);	/* set the height of the image */
    TIFFSetField (out, TIFFTAG_SAMPLESPERPIXEL, sampleperpixel);	/* set number of channels per pixel */
    TIFFSetField (out, TIFFTAG_BITSPERSAMPLE, 8);	/* set the size of the channels */
    TIFFSetField (out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);	/* set the origin of the image */
	
    /* Some other essential fields to set */
    TIFFSetField (out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
    TIFFSetField (out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
	
    /* determining the dimension of a single row */
    linebytes = sampleperpixel * width;	/* length in memory of one row of pixel in the image */
	
    /* allocating memory to store the pixels of current row */
    if (TIFFScanlineSize (out) == linebytes)
		buf = (unsigned char *) _TIFFmalloc (linebytes);
    else
		buf = (unsigned char *) _TIFFmalloc (TIFFScanlineSize (out));
	
    /* set the strip size of the file to be size of one row of pixels */
    TIFFSetField (out, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize (out, width * sampleperpixel));
	
    /* Writing image to the file one strip at a time */
    for (row = 0; row < height; ++row) {
		memcpy (buf, &image [(height - row - 1) * linebytes], linebytes);	/* tricky index */
		if (TIFFWriteScanline (out, buf, row, 0) < 0)
			break;
    }
    
    (void) TIFFClose (out);
    
    if (buf)
		_TIFFfree (buf);
}

/**
 * Private function. Create a Gaussian point
 * @param gaussian parameters of Gaussian
 * @param x first coordinate of the point
 * @param y second coordinate of the point
 * @return the Guassian point
 */

unsigned char createPoint (float *gaussian, int x, int y) {

    float x_arg = pow (( (float) x - gaussian [PAR_X]), 2.0) / pow (gaussian [PAR_SX], 2.0);
    float y_arg = pow (( (float) y - gaussian [PAR_Y]), 2.0) / pow (gaussian [PAR_SY], 2.0);
    float arg = - (x_arg + y_arg);
    
    return (unsigned char) gaussian [PAR_A] * exp (arg);
}

/**
 * Private function. Return the image of the Gaussian as specified in the input parameters
 * @param width width of the image
 * @param height height of the image
 * @param parameters parameters of gaussian
 * @ret matrix representation of the gaussian as a 8bit image (unsigned char)
 */

unsigned char *createMatrix (int width, int height, float *parameters) {

    int i = 0, j = 0;
    int dim = width * height;
    unsigned char *matrix = (unsigned char *) malloc (dim);
    unsigned char *p = matrix;
    
    /* build the image */
    for (i = 0; i < height; ++i)
		for (j = 0; j < width; ++j)
			*p++ = createPoint (parameters, j, i);
    return matrix;
}

/* 
 * Calculate the time difference in microseconds between time_1 and time_2
 * @param time_1 first time
 * @param time_2 second time
 * @ret microseconds
 */

long int getCompletionTime (struct timeval* time_1, struct timeval* time_2) {

    long int microseconds = 0;

    microseconds += time_2->tv_sec * MICROSECONDSPERSECOND;
    microseconds += time_2->tv_usec;

    microseconds -= time_1->tv_sec * MICROSECONDSPERSECOND;
    microseconds -= time_1->tv_usec;
    
    return microseconds;
}

/**
 * Given a specified width and length return a representation of the Gaussian
 * @param width width of the image
 * @height height of the image
 * @ret	representation of the Gaussian
 */

unsigned char* createImage (int width, int height) {
	
    float parameters [DIM_PAR];
	int i;
	
	for (i = 0; i < DIM_PAR; ++i) 
        parameters [i] = 0;

	/* values of the gaussian are chosen with respect of the dimension of the image in order to have a nice centered gaussian */ 	
	parameters [PAR_A] = AVERAGE + ( rand() % DEVIATION ) - DEVIATION/2;    /* random amplitude value with mean AVERAGE and a maximum deviation of DEVIATION */
	parameters [PAR_X] = width/2;
	parameters [PAR_Y] = height/2;
	parameters [PAR_SX] = width/4;
	parameters [PAR_SY] = height/4;
	
    /* image representing the Gaussian fit */
    return createMatrix (height, width, parameters);
}
